-------------------
-- 文件读写模块
-------------------
local M = {}

local crypto = require("crypto")

local json = require("json")

local lfs = require("lfs")

-- local securityMod = require("tools.security")

--[[删除末尾的斜杠
	（对于lfs，如果末尾有/ 例如"assets/"，则会判定文件夹不存在）
	deleteEndSlash("path/aaa/") -> path/aaa
]]
local function deleteEndSlash(str)
	local resultStr = str
	local len = str:len()

	local startPos, endPos = string.find(str, "/", -1)
	if startPos and endPos and endPos == len then
		resultStr = string.sub(str, 1, startPos - 1)
	end

	return resultStr
end

local tSecurityKey = {
	[1] = {
		key1 = "common", key2 = "22521332353151350035103310333932103184", key3 = "images/loginBg.jpg",
		key4 = "json", key5 = "26128033463239365235913194338633813458", key6 = "!@#5%hK",
		key7 = "#", key8 = "$", key9 = "<wulinbatu>", key10 = "*", key11 = "test"
	},
	[2] = {
		key1 = "tools", key2 = "22521332353151350035103310333932103184", key3 = "assets/pack/loginBg.jpg",
		key4 = "xdg1", key5 = "26128033463239365235913194338633813458", key6 = "!@#5%hK",
		key7 = "&", key8 = "*", key9 = "<wulinbatu>", key10 = "#", key11 = "logicu"
	},
}
function M.produceHash(jsonData, keyType)
	local keyType = keyType or 1
	local keys = tSecurityKey[keyType]
	local wulin1 = "1111" --crypto.hmac( crypto.sha256, keys.key1 .. securityMod.Decrypt(keys.key2), keys.key3)
	local wulin2 = "2222" --crypto.hmac( crypto.sha512, keys.key4 .. securityMod.Decrypt(keys.key5), keys.key6 )
	local hashStr = crypto.digest( crypto.sha1, keys.key7 .. wulin1 .. keys.key8 .. jsonData .. keys.key9 .. wulin2 .. keys.key10 ..crypto.digest( crypto.sha384, keys.key11 ))

	return hashStr
end

-- 读取文件
function M.readFile(params)
	local fileName = params.fileName
	local readMod = params.readMod or "rb"
	local basePath = params.basePath or system.DocumentsDirectory

	local path = system.pathForFile(fileName, basePath)

	local contents = nil

	local file = nil

	if path then
		file = io.open(path, readMod)
	end

	if file then
		contents = file:read("*a")
		file:flush()
		io.close(file)
		file = nil
	end

	return contents
end

function M.verifyFile(params)
	local fileName = params.fileName
	local contents = params.contents
	local basePath = params.basePath or system.DocumentsDirectory
	local readMod = params.readMod or "rb"
	local hashKeyType = params.hashKeyType or 1
	
	-- 读取hash文件中的hash串
	print("verifyFile read file" .. fileName .. 'hash')
	local hashStr = M.readFile({
		fileName = fileName.."hash", 
		basePath = basePath,
		readMod = readMod
	})

	if hashStr then
		-- 根据被保护文件的内容，重新计算一次hash值
		local realHash = M.produceHash(contents, hashKeyType)

		print("hashStr = ", hashStr)
		print("realHash = ", realHash)

		-- 验证两个hash值是否相同
		if hashStr == realHash then
			print(fileName.." verifyFile true!")
			return true
		end
	end

	-- 校验失败
	print(fileName.." verifyFile false!")
	return false
end

--[[刷新hash文件的内容
--]]
function M.createHashFile(params)
	local fileName = params.fileName
	local jsonData = params.jsonData
	local basePath = params.basePath or system.DocumentsDirectory
	local hashKeyType = params.hashKeyType or 1
	local writeMod = params.writeMod or "wb"

	local path = system.pathForFile( fileName .. "hash", basePath)

	local hashStr = M.produceHash(jsonData, hashKeyType)

	print("createHashFile = ", hashStr)

	local wfh = io.open(path, writeMod)

	if wfh then
		wfh:write(hashStr)
		wfh:flush()
		wfh:close()
		wfh = nil
	end
end

function M.readNewFile( fileName, basePath)
	local basePath = basePath or system.DocumentsDirectory

	local contents = M.readFile({fileName = fileName, basePath = basePath, readMod = "r"})

	if contents then
		local isVerifySuccess = M.verifyFile({
			fileName = fileName, 
			contents = contents, 
			basePath = basePath, 
			hashKeyType = 1, 
			readMod = "r"
		})

		if isVerifySuccess then
			-- json解码
			contents = json.decode(contents)
		else

		end
	end

	return contents
end


--[[保存数据到指定的文件，并为之创建一个"文件名".."hash"的保护文件
	当读取该文件时，通过hash文件的hash值来验证被保护文件中的数据是否合法

	参数说明:
		filename:保存的文件名
		data:保存的数据（已转换为JSON）
		base:指定目录
--]]
function M.saveFile(fileName, data, basePath)
	local basePath = basePath or system.DocumentsDirectory
	local jsonData = data or ''

	if type(jsonData) ~= 'string' then
		jsonData = json.encode(jsonData)
	end

	-- 创建对应的hash文件
	M.createHashFile({
		fileName = fileName, 
		jsonData = jsonData, 
		basePath = basePath,
		writeMod = "w+",
		hashKeyType = 1,
	})

	local fullPath = system.pathForFile( fileName, basePath)
	local wfh = io.open(fullPath, "w+")

	if wfh then
		wfh:write( jsonData )
		wfh:flush()
		wfh:close()
		wfh = nil
	end
end

--[[判定文件夹是否存在
	path：文件夹名称 的路径名最后不能加"/",比如有"patch"文件夹，但是传入是"patch/"判定结果是不存在
	basePath：基础路径 默认为 system.DocumentsDirectory
	isDirExist({path = "assets/unpcak"})
]]
function M.isDirExist(params)
	local path = deleteEndSlash(params.path)
	local basePath = params.basePath or system.DocumentsDirectory
	local fullPath = system.pathForFile(path, basePath)

	if not fullPath then
		return false
	end
	
	local state, msg = lfs.attributes(fullPath)

	if not state or state.mode ~= "directory" then
		print("isDirExist:", path .. " not exist:", msg)
		return false
	end

	return true
end

--[[
	循环遍历一个目录
	path：文件夹路径
	listener(path, fileName, content) 函数回调
		path：文件基础路径
		fileName：文件名
		content：文件内容
	tExcept：不去遍历的文件夹或者是文件
	basePath：基础路径 默认system.DocumentsDirectory（可选参数）
	notReadFileContent：是否不读取文件内容（如果只是遍历获取文件名称的话可以节省时间）
]] 
function M.iterateDir(params)
	local path = params.path
	local listener = params.listener or function() end
	local tExcept = params.tExcept or {}
	local notReadFileContent = params.notReadFileContent or false -- 如果不读文件内，那么listener 回传的content会是nil

	local basePath = params.basePath or system.DocumentsDirectory
	local dirPath = system.pathForFile(path, basePath)

	if not M.isDirExist({path = path}) then
		return
	end
	
	for fileName in lfs.dir(dirPath) do
		if fileName ~= "." and fileName ~= ".." then
			local mode = lfs.attributes(dirPath .. "/" .. fileName).mode

			if tExcept[fileName] then

			elseif mode and mode == "directory" then
				M.iterateDir({
					path = path .. "/" .. fileName, 
					listener = listener, 
					tExcept = tExcept
				})
			else
				local content

				if notReadFileContent then
					content = nil
				else
					content = M.readFile({fileName = path .. "/" .. fileName, basePath = basePath}) or ""
				end

				listener(path, fileName, content)
			end
		end
	end
end

--[[迭代子文件夹
	与iterateDir不同的是，它只会迭代遍历文件夹，不会遍历文件
	path
	listener(dirName, dirPath) -- dirName 子文件夹名称  dirPath 子文件夹路径
]]
function M.iteratorChildDir(params)
	local path = params.path
	local listener = params.listener or function() end
	
	local fullPath = system.pathForFile(path, system.DocumentsDirectory)

	for file in lfs.dir(fullPath) do
		if file ~= "." and file ~= ".." then
			local attr = lfs.attributes(fullPath .. "/" .. file)

			if attr and attr.mode and attr.mode == "directory" then
				listener(file, path .. "/" .. file)
			end
		end
	end
end

-- 删除某个文件
function M.deteleFile(path, basePath)
	local basePath = basePath or system.DocumentsDirectory
	local fullPath = system.pathForFile(path, basePath)
	if fullPath and type(fullPath) == "string" then
		local result, msg = os.remove(fullPath)
	end
end

--[[删除一个目录下所有文件
	isDelDir(是否顺带删除文件夹，默认不删除)
--]]
function M.deleteAllFile(dirPath, isDelDir)
	if not M.isDirExist({path = dirPath}) then
		return
	end
	local path = system.pathForFile(dirPath, system.DocumentsDirectory)
	for fileName in lfs.dir(path) do
		if fileName ~= "." and fileName ~= ".." then
			local mode = lfs.attributes(path .. "/" .. fileName).mode
			if mode and mode == "directory" then
				M.deleteAllFile(dirPath .. "/" .. fileName, isDelDir)
				if isDelDir then
					lfs.rmdir(path .. "/" .. fileName)
				end
			else
				os.remove(path .. "/" .. fileName)
			end
		end
	end

	if isDelDir then
		lfs.rmdir(path)
	end
end

--[[判定是否存在某个文件
	注意此判定不适用于system.ResourceDirectory
	fileName：文件名
	basePath：基础路径 默认system.DocumentsDirectory（可选参数）
]]
function M.isFileExist(params)
	local fileName = params.fileName
	local basePath = params.basePath or system.DocumentsDirectory

	local filePath = system.pathForFile(fileName, basePath)
	local handle, msg = io.open(filePath, "rb")

	-- if handle then
		local text = display.newText({text = msg .. " aaaaa", x = SCREEN_CX, y = SCREEN_CY, width = 600})
	-- end

	if not handle then
		return false
	else
		handle:flush()
		io.close(handle)
		handle = nil
		return true
	end
end

--[[ 文件写入
	fileName：文件名
	fileContent：文件内容
	basePath：基础路径 默认system.DocumentsDirectory（可选参数）
	writeMod：写入方式 默认"wb"（可选参数）
	writeFile({fileName = "aaa.txt", fileContent = "this is content"})
]]
function M.writeFile(params)
	local fileName = params.fileName
	local fileContent = params.fileContent or ""
	local basePath = params.basePath or system.DocumentsDirectory
	local writeMod = params.writeMod or "wb"
	
	if type(fileContent) == "table" then
		fileContent = json.encode(fileContent)
	end
	
	local filePath = system.pathForFile(fileName, basePath)
	local handle, msg = io.open(filePath, writeMod)

	if not handle then
		return nil
	end

	handle:write(fileContent)
	handle:flush()
	handle:close()
	handle = nil
end

--[[创建一个文件夹
	dirPath：目录路径
]]
function M.createDir(params)
	local path = params.path
	local fullPath = system.pathForFile(path, system.DocumentsDirectory)

	lfs.mkdir(fullPath)
end


return M